1 using System;
2 using
System.Collections.Generic;
3 using
UnityEngine;
4 using
UnityEngine.Assertions;
5
6 namespace
ProceduralToolkit.Examples
7 {

8     ///
<summary>
9     ///
Fully procedural building generator
10     ///
</summary>
11     
public class BuildingGenerator : BuildingGeneratorBase
12     {
13         
private const float socleHeight = 1;
14         
private const float floorHeight = 2.5f;
15         
private const float atticHeight = 1;
16
17         
private Dictionary<PanelType, List<Func<IFacadePanel>>> constructors =
18             
new Dictionary<PanelType, List<Func<IFacadePanel>>>();
19         
private Dictionary<PanelType, Func<IFacadePanel>> commonConstructors =
20             
new Dictionary<PanelType, Func<IFacadePanel>>();
21
22         
private Dictionary<PanelSize, float> sizeValues = new Dictionary<PanelSize, float>
23         {
24             {PanelSize.Narrow,
2.5f},
25             {PanelSize.Wide,
3},
26         };
27
28         
public MeshDraft Generate(Config config)
29         {
30             Assert.IsTrue(config.width >
0);
31             Assert.IsTrue(config.length >
0);
32             Assert.IsTrue(config.floors >
0);
33             Assert.IsTrue(config.entranceInterval >
0);
34
35             InitializeConstructors(config.palette);
36
37             
var foundationPolygon = new List<Vector2>
38             {
39                 Vector2.left*config.length/
2 + Vector2.down*config.width/2,
40                 Vector2.right*config.length/
2 + Vector2.down*config.width/2,
41                 Vector2.right*config.length/
2 + Vector2.up*config.width/2,
42                 Vector2.left*config.length/
2 + Vector2.up*config.width/2
43             };
44
45             commonConstructors[PanelType.Entrance] = constructors[PanelType.Entrance].GetRandom();
46             commonConstructors[PanelType.EntranceWindow] = constructors[PanelType.EntranceWindow].GetRandom();
47
48             
var facadeLayouts = new List<FacadeLayout>();
49             
for (int i = 0; i < foundationPolygon.Count; i++)
50             {
51                 Vector2 a = foundationPolygon[i];
52                 Vector2 b = foundationPolygon.GetLooped(i +
1);
53                 
float? entranceInterval = i == 0 ? config.entranceInterval : (float?) null;
54                 
bool hasBalconies = RandomE.Chance(0.5f);
55                 facadeLayouts.Add(GenerateFacade(a, b, config.floors, hasBalconies, config.hasAttic, entranceInterval));
56             }
57
58             
float facadeHeight = floorHeight*config.floors + socleHeight + (config.hasAttic ? atticHeight : 0);
59
60             
var buildingDraft = GenerateFacades(foundationPolygon, facadeLayouts);
61             buildingDraft.uv.Clear();
62
63             
var roof = RoofGenerator.Generate(foundationPolygon, facadeHeight, config.roofConfig);
64             roof.Paint(config.palette.roofColor);
65             buildingDraft.Add(roof);
66
67             
return buildingDraft;
68         }
69
70         
private void InitializeConstructors(Palette palette)
71         {
72             constructors[PanelType.Wall] =
new List<Func<IFacadePanel>>
73             {
74                 () =>
new ProceduralWall(palette.wallColor)
75             };
76             constructors[PanelType.Window] =
new List<Func<IFacadePanel>>
77             {
78                 () =>
new ProceduralWindow(palette.wallColor, palette.frameColor, palette.glassColor)
79             };
80             constructors[PanelType.Balcony] =
new List<Func<IFacadePanel>>
81             {
82                 () =>
new ProceduralBalcony(palette.wallColor, palette.frameColor, palette.glassColor),
83                 () =>
new ProceduralBalconyGlazed(palette.wallColor, palette.frameColor, palette.glassColor,
84                     palette.roofColor)
85             };
86             constructors[PanelType.Entrance] =
new List<Func<IFacadePanel>>
87             {
88                 () =>
new ProceduralEntrance(palette.wallColor, palette.doorColor),
89                 () =>
new ProceduralEntranceRoofed(palette.wallColor, palette.doorColor, palette.roofColor)
90             };
91             constructors[PanelType.EntranceWindow] =
new List<Func<IFacadePanel>>
92             {
93                 () =>
new ProceduralEntranceWindow(palette.wallColor, palette.frameColor, palette.glassColor),
94                 () =>
new ProceduralWindow(palette.wallColor, palette.frameColor, palette.glassColor)
95             };
96             constructors[PanelType.EntranceWallLast] =
new List<Func<IFacadePanel>>
97             {
98                 () =>
new ProceduralWall(palette.wallColor)
99             };
100             constructors[PanelType.Socle] =
new List<Func<IFacadePanel>>
101             {
102                 () =>
new ProceduralSocle(palette.socleColor),
103                 () =>
new ProceduralSocleWindowed(palette.socleColor, palette.socleWindowColor)
104             };
105             constructors[PanelType.Attic] =
new List<Func<IFacadePanel>>
106             {
107                 () =>
new ProceduralAtticVented(palette.wallColor, palette.roofColor),
108                 () =>
new ProceduralWall(palette.wallColor)
109             };
110         }
111
112         
private FacadeLayout GenerateFacade(
113             Vector2 a,
114             Vector2 b,
115             
int floors,
116             
bool hasBalconies,
117             
bool hasAttic,
118             
float? entranceInterval)
119         {
120             
float facadeWidth = (b - a).magnitude;
121             List<PanelSize> panelSizes = DivideFacade(sizeValues, facadeWidth);
122
123             
var facadeLayout = GenerateFacadeChunk(facadeWidth, panelSizes, floors, hasBalconies, hasAttic,
124                 entranceInterval);
125             facadeLayout.origin = Vector2.zero;
126             
return facadeLayout;
127         }
128
129         
private FacadeLayout GenerateFacadeChunk(
130             
float facadeWidth,
131             List<PanelSize> panelSizes,
132             
int floors,
133             
bool hasBalconies,
134             
bool hasAttic,
135             
float? entranceInterval)
136         {
137             
var facadeLayout = new VerticalLayout();
138             
float facadeHeight = 0;
139
140             
if (entranceInterval.HasValue)
141             {
142                 
int entranceCount = Mathf.Max(Mathf.FloorToInt(facadeWidth/entranceInterval.Value) - 1, 1);
143                 
int entranceIndexInterval = (panelSizes.Count - entranceCount)/(entranceCount + 1);
144
145                 
var horizontal = new HorizontalLayout();
146                 
int lastEntranceIndex = -1;
147                 
for (int i = 0; i < entranceCount; i++)
148                 {
149                     
int entranceIndex = (i + 1)*entranceIndexInterval + i;
150
151                     horizontal.Add(ConstructFacadeChunk(panelSizes, lastEntranceIndex +
1, entranceIndex, floors,
152                         hasBalconies));
153
154                     horizontal.Add(ConstructEntranceVertical(sizeValues[panelSizes[entranceIndex]], floors));
155
156                     
if (i == entranceCount - 1)
157                     {
158                         horizontal.Add(ConstructFacadeChunk(panelSizes, entranceIndex +
1, panelSizes.Count, floors,
159                             hasBalconies));
160                     }
161
162                     lastEntranceIndex = entranceIndex;
163                 }
164
165                 facadeLayout.Add(horizontal);
166                 facadeHeight += socleHeight + floors*floorHeight;
167             }
168             
else
169             {
170                 
var socle = ConstructHorizontal(
171                     constructors: constructors[PanelType.Socle],
172                     height: socleHeight,
173                     getPanelWidth: index => sizeValues[panelSizes[index]],
174                     count: panelSizes.Count);
175
176                 facadeLayout.Add(socle);
177                 facadeHeight += socleHeight;
178
179                 
for (int floorIndex = 0; floorIndex < floors; floorIndex++)
180                 {
181                     HorizontalLayout floor;
182                     
if (floorIndex == 0)
183                     {
184                         floor = ConstructHorizontal(
185                             constructors: constructors[PanelType.Window],
186                             height: floorHeight,
187                             getPanelWidth: index => sizeValues[panelSizes[index]],
188                             count: panelSizes.Count);
189                     }
190                     
else
191                     {
192                         floor = ConstructHorizontal(
193                             constructors: hasBalconies
194                                 ? constructors[PanelType.Balcony]
195                                 : constructors[PanelType.Window],
196                             height: floorHeight,
197                             getPanelWidth: index => sizeValues[panelSizes[index]],
198                             count: panelSizes.Count);
199                     }
200
201                     facadeLayout.Add(floor);
202                     facadeHeight += floorHeight;
203                 }
204             }
205
206             
if (hasAttic)
207             {
208                 
var attic = ConstructHorizontal(
209                     constructors: constructors[PanelType.Attic],
210                     height: atticHeight,
211                     getPanelWidth: index => sizeValues[panelSizes[index]],
212                     count: panelSizes.Count);
213
214                 facadeLayout.Add(attic);
215                 facadeHeight += atticHeight;
216             }
217
218             facadeLayout.width = facadeWidth;
219             facadeLayout.height = facadeHeight;
220             
return facadeLayout;
221         }
222
223         
private VerticalLayout ConstructEntranceVertical(float width, int floors)
224         {
225             
var vertical = ConstructVertical(
226                 constructor: commonConstructors[PanelType.EntranceWindow],
227                 width: width,
228                 panelHeight: floorHeight,
229                 count: floors -
1);
230
231             
var entrance = commonConstructors[PanelType.Entrance]();
232             entrance.height = floorHeight;
233             vertical.Insert(
0, entrance);
234
235             vertical.Add(constructors[PanelType.EntranceWallLast].GetRandom()());
236             
return vertical;
237         }
238
239         
private FacadeLayout ConstructFacadeChunk(List<PanelSize> panelSizes, int from, int to, int floors,
240             
bool hasBalconies)
241         {
242             
var sizes = panelSizes.GetRange(from, to - from);
243             
float chunkWidth = 0;
244             
foreach (var size in sizes)
245             {
246                 chunkWidth += sizeValues[size];
247             }
248             
return GenerateFacadeChunk(chunkWidth, sizes, floors, hasBalconies, false, null);
249         }
250
251         
private static List<PanelSize> DivideFacade(Dictionary<PanelSize, float> sizeValues, float facadeWidth)
252         {
253             Dictionary<PanelSize,
int> knapsack = PTUtils.Knapsack(sizeValues, facadeWidth);
254             
var sizes = new List<PanelSize>();
255             
foreach (var pair in knapsack)
256             {
257                 
for (var i = 0; i < pair.Value; i++)
258                 {
259                     sizes.Add(pair.Key);
260                 }
261             }
262             sizes.Shuffle();
263             
return sizes;
264         }
265
266         
[Serializable]
267         
public class Config
268         {
269             
public float width = 12;
270             
public float length = 36;
271             
public int floors = 5;
272             
public float entranceInterval = 12;
273             
public bool hasAttic = true;
274             
public RoofConfig roofConfig = new RoofConfig
275             {
276                 type = RoofType.Flat,
277                 thickness =
0.2f,
278                 overhang =
0.2f,
279             };
280             
public Palette palette = new Palette();
281         }
282
283         
[Serializable]
284         
public class Palette
285         {
286             
public Color socleColor = ColorE.silver;
287             
public Color socleWindowColor = (ColorE.silver/2).WithA(1);
288             
public Color doorColor = (ColorE.silver/2).WithA(1);
289             
public Color wallColor = ColorE.white;
290             
public Color frameColor = ColorE.silver;
291             
public Color glassColor = ColorE.white;
292             
public Color roofColor = (ColorE.gray/4).WithA(1);
293         }
294
295         
private enum PanelSize
296         {
297             Narrow,
298             Wide
299         }
300
301         
private enum PanelType
302         {
303             Wall,
304             Window,
305             Balcony,
306             Entrance,
307             EntranceWindow,
308             EntranceWallLast,
309             Socle,
310             Attic,
311         };
312     }
313 }


Gõ tìm kiếm nhanh...